Practice Problems and Solutions 3
You should do these problems on paper or mentally first, and only THEN check the solution.
(Optional Deep Background) Scope of local variables in Python and Java;
This document is review of the notion of scope and local variables in Python and Java, and an explanation of how these relate to (and are explained by!) let expression and lambda expressions.
Definition: A local variable is one defined inside a function, a class, or a statement block; its scope is the part of the program where it may be referred to without error.
Local Variables and Scope in Java
Consider the following short Java program, which simply prints out the number 15:
public class ScopeTest {
public static void main(String args[]) {
int x = 5;
System.out.println( x * 2 + x );
}
}
3.1. What is the rule for the scope of x? Draw a vertical line showing the scope of x.
Show Solution
The scope of a local variable is from the point of its definition to the end of the closest enclosing curly brace:
public class ScopeTest {
public static void main(String args[]) {
int x = 5; * scope of x
*
System.out.println( x * 2 + x ); *
}
}
3.2. Ditto for x and y in:
public class ScopeTest {
public static void main(String args[]) {
int y = 2;
int x = 3 * y;
System.out.println(x + y);
}
}
Show Solution
public class ScopeTest {
public static void main(String args[]) {
int y = 2; * scope of y
*
int x = 3 * y; * * scope of x
* *
System.out.println(x + y); * *
}
}
3.3. Ditto for x,y, and z in:
public class ScopeTest {
public static void main(String args[]) {
int x = 3;
int y = x - 2;
int z = 5 * y;
System.out.println( x * y + z );
}
}
3.
3.
Local variables also occur in function definitions and statement blocks (statements inside curly braces, perhaps in a loop or conditional). Consider the following:
public class ScopeTest {
public static void main(String args[]) {
int y = 2;
if(y >= 0) {
int x = 3 * y;
System.out.println(x + y);
}
System.out.println("bye!");
}
}
3.4. Show the scope of the variables x and y in this last example.
Show Solution
public class ScopeTest {
public static void main(String args[]) {
int y = 2; * scope of y
*
if(y >= 0) { *
*
int x = 3 * y; * * scope of x
* *
System.out.println(x + y); * *
} *
*
System.out.println("bye!"); * <= note that you can not refer to x here
}
}
3.5. Finally, local variables may be created as parameters of functions; show the
scope of x, y, and z in this example (remembering that the rule for scope of members of a class is the whole class):
public class ScopeTest {
public static int f(int y) {
System.out.println("hi");
if(y >= 0) {
int z = 5 * y;
return ( x * y + z );
}
System.out.println("there!");
return y;
}
public static int x = 3;
public static void main(String args[]) {
System.out.println( f( x - 2 ) );
System.out.println("bye!");
}
}
Show Solution
public class ScopeTest {
* scope of x (whole class)
*
public static int f(int y) { * * scope of y
System.out.println("hi"); * *
if(y >= 0) { * *
int z = 5 * y; * * * scope of z
return ( x * y + z ); * * *
} * *
System.out.println("there!"); * *
return y; * *
} *
*
public static int x = 3; *
*
public static void main(String args[]) { *
System.out.println( f( x - 2 ) ); *
System.out.println("bye!"); *
} *
}
Variables (not necessarily local) and Scope in Python
Now let us see how this all works in Python... Consider the following Python program (either typed into the interpreter, run from a file, or run from a code cell in a notebook):
x = 5
print( x * 2 + x )
3.6. What is the rule for the scope of x defined at the top level (i.e., not in a function)? Draw a vertical line showing the scope of x.
Show Solution
The scope of a variable declared at the top level is from the point of definition to the end of the file/cell/interaction:
x = 5 * scope of x
*
print( x * 2 + x ) *
*
*
3.7. Ditto for x and y in:
y = 2
x = 3 * y
print(x + y)
Show Solution
y = 2 * scope of y
*
x = 3 * y * * scope of x
* *
print(x + y) * *
* *
3.8. Ditto for x,y, and z in:
x = 3
y = x - 2
z = 5 * y
print( x * y + z )
Show Solution
x = 3 * scope of x
*
y = x - 2 * * scope of y
* *
z = 5 * y * * * scope of z
* * *
print( x * y + z ) * * *
* * *
Python also has function definitions.
3.9. Show the scope of the variables x and y in this example:
y = 2
def f(x):
print(x + y)
f(3 * y)
Show Solution
y = 2 * scope of y
*
def f(x): * * scope of x
print(x + y) * *
*
f(3 * y) *
*
3.10. In Python (but not in Java) functions can be defined locally inside other functions; show the
scope of x, y, and z in this example:
x = 3
def f(y):
# this is a function g local to f
def g(z):
return x * y + z
return g(5*y)
print( f(3) )
Show Solution
x = 3 * scope of x
*
def f(y): * * scope of y
# this is a function g local to f * *
def g(z): * * * scope of z
return x * y + z * * *
* *
return g(5*y) * *
*
print( f(3) ) *
Hole in Scope Issue in Java and Python
When local variables have the same name in different scopes, we can get a "hole in scope" phenomenon, where the outer definition is hidden by an inner definition, because you when you look for a variable definition, you look outwards from the use to the CLOSEST ENCLOSING DEFINITION.
3.11. Show the scope of the three DIFFERENT variables called x in the following Java program:
public class ScopeTest {
public static int x = 3;
public static int f(int x) {
return x + 1;
}
public static int g(int z) {
int x = z + 1;
return x;
}
public static void main(String args[]) {
System.out.println( f( 2 * x ) );
System.out.println( g( 2 * x ) );
}
}
Show Solution
public class ScopeTest {
* scope of class member x
public static int x = 3; *
*
public static int f(int x) { * scope of function parameter x
return x + 1; *
*
}
*
public static int g(int z) {
int x = z + 1; * scope of local variable x
return x; *
}
*
public static void main(String args[]) { *
*
System.out.println( f( 2 * x ) ); *
System.out.println( g( 2 * x ) ); * *
} *
*
}
3.12. Show the scope of the two DIFFERENT variables called x in the following Python program:
x = 3
def f(x):
return x + 1
print( f( 2 * x ) )
Show Solution
x = 3 * scope of global x
*
def f(x): * scope of parameter x
return x + 1 *
*
print( f( 2 * x ) ) *
(End of optional section on scope in Java and Python)
Lambda Expressions
Lambda expressions were introduced using Haskell syntax.
Lambda calculus: bound and free variables, reduction rules.
Let us say that a variable x
occurs as a bound variable in a lambda expression (\x -> expr)
, where
"\x ->" is the binding for the variable x, and
the scope of the binding is expr
; and a variable y
in a lambda expression
which does not occur in the scope of any bound variable is called a free variable.
For the following lambda expressions, circle each free variable, and for each bound variable, draw a line from the occurrence of the variable in the body to the corresponding lambda binding.
3.13. (\y -> (\x -> y x)) y
Show Solution
3.14. (\x -> x y) (\y -> y x)
Show Solution
3.15. (\x -> (\x -> (\y -> x y)) (\z -> (y z) x))
Show Solution
3.16. Draw the abstract syntax tree for the lambda expression in problem 3.13.
Show Solution
3.17. Ditto for 3.14
Show Solution
3.18. Ditto for 3.15
Show Solution
For the following lambda expressions, perform a single step of reduction and show the substitution that was used. Choose the left-most possible reduction.
3.19. (\x -> (\y -> y x)) z
Show Solution
(\x -> (\y -> y x))) z
=> (\y -> y z)
subst: (x,z)
3.20. (\x -> x) ((\x -> x) ((\x -> x) z))
Show Solution
(\x -> x) ((\x -> x) ((\x -> x) z))
=> ((\x -> x) ((\x -> x) z))
subst = (x, ((\x -> x) ((\x -> x) z)))
3.21. The lambda expression in problem 3.14.
Show Solution
(\x -> x y) (\y -> y x)
=> (\y -> y x) y
subst = ( x, (\y -> y x))
3.22. (\x -> x (y x)) (\y -> y z)
Show Solution
(\x -> x (y x)) (\y -> y z)
=> (\y -> y z) (y (\y -> y z))
subst = ( x, (\y -> y z))
3.23. (\x -> x (\x -> y x)) z
Show Solution
(\x -> x (\x -> y x)) z
=> z (\x -> y x)) -- note that the inner binding for x creates a "hole in scope" for the outer binding
subst = ( x, z)
3.24. (\x -> ((x (\x -> y x)) x) x) y
Show Solution
(\x -> ((x (\x -> y x)) x) x) y
=> ((y (\x -> y x)) y) y -- note that the inner binding for x creates a "hole in scope" for the outer binding
subst = ( x, y)
3.25. (\x -> (x (\y -> y x)) x) z
Show Solution
(\x -> (x (\y -> y x)) x) z
=> (z (\y -> y z)) z -- note that the inner binding for x creates a "hole in scope" for the outer binding
subst = ( x, z)
For the following lambda expressions, reduce the expression step by step (choosing the
left-most possible application each time) until no more reductions can be performed.
You won't be expected to do such multiple reductions on Midterm 1.
3.26. (\x -> x (x z)) (\y -> y)
Show Solution
(\x -> x (x z)) (\y -> z y)
=> (\y -> y) ((\y -> y) z)
=> ((\y -> y) z)
=> z
3.27. (\x -> x (x z)) (\y -> z y)
Show Solution
(\x -> x (x z)) (\y -> z y)
=> (\y -> z y) ((\y -> z y) z)
=> z ((\y -> z y) z)
=> z (z z)
3.28. The lambda expression in 3.20.
Show Solution
((\x -> x) ((\x -> x) ((\x -> x) z)))
=> ((\x -> x) ((\x -> x) z))
=> ((\x -> x) z)
=> z
3.29. The lambda expression in 3.15
Show Solution
(\x -> (\x -> (\y -> (x y)))(\z -> (y z) x))
=> (\x -> (\y -> (\z -> (y z) x) y))
=> (\x -> (\y -> (y y) x))
3.30. (\f -> (\x -> f x) ) (\f -> (\y -> f y) )
Show Solution
(\f -> (\x -> f x))(\f -> (\y -> f y))
=> (\x -> (\f -> (\y -> f y)) x)
=> (\x -> (\y -> x y))
3.31. (\f -> (\x -> f (f (f x) ) ) ) (\x -> z x)
Show Solution
(\f -> (\x -> f (f (f x))))(\x -> z x)
=> (\x -> (\x -> z x)((\x -> z x)((\x -> z x) x)))
=> (\x -> z ((\x -> z x)((\x -> z x) x)))
=> (\x -> z (z ((\x -> z x) x)))
=> (\x -> z (z (z x)))
3.32. (\f -> f (\x -> (\y -> y) ) (\x -> (\y -> x) ) ) (\x -> (\y -> x) ) /
Show Solution
(\f -> f (\x -> (\y -> y))(\x -> (\y -> x)))(\x -> (\y -> x))
=> (\x -> (\y -> x))(\x -> (\y -> y))(\x -> (\y -> x))
=> (\y -> (\x -> (\y -> y)))(\x -> (\y -> x))
=> (\x -> (\y -> y))
Let Expressions with a single binding; Relationship between Lambda and Let Expressions
In Haskell you can set up local variables using a let
expression. In the next series of problems, we explore this idea and show its correspondence with a certain kind of lambda expression (showing that lambda expressions really are the "assembly language of functional programming").
Recall that the let expression with a single binding has the following syntax:
let <var> = <expression> in <body>
The rule for the scope of the local variable is simple: the scope is the body.
The body can be any expression or even another let expression; here are some examples:
let x = 5 in x * 2 + x
let y = 2 in let x = 3 * y in x + y
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
The nesting in the last one is clearer if we add parentheses:
(let x = 2 in (let y = x - 2 in (let z = 9 in x * y + z)) )
--------------------------------------------- body of (let x = ... )
------------------------ body of (let y = ... )
--------- body of (let z = ... )
For the following, first show the scope of the bound variables in the expression, and then calculate its value, doing all beta-reductions first and arithmetic operations last. Show the substitution and how itis applied to the expression.
3.33.
let x = 5 in x * 2 + x,
Show Solution
let x = 5 in x * 2 + x
--------- scope of binding for x
=> x * 2 + x substitution: (x,5)
---------
body
= 5 * 2 + 5 apply substitution
= 15 evaluate
3.34.
let x = 8 in x - (3 * x),
Show Solution
let x = 8 in x - (3 * x)
----------- scope of binding for x
=> x - (3 * x) substitution: (x,8)
-----------
body
= 8 - (3 * 8) apply substitution
= 8 - 24 = -16 evaluate
3.35.
let x = (3 * 2) in (x-1) * x,
Show Solution
let x = (3 * 2) in (x-1) * x
--------- scope of binding for x
=> (x-1)*x substitution: (x,(3*2))
-------
body
= ((3*2)-1)*(3*2) apply substitution
= (6-1)*6 = 5*6 = 30 evaluate
3.36.
let y = 2 in let x = 3 * y in x + y
Show Solution
let y = 2 in let x = 3 * y in x + y
---------------------- scope of the binding for y
----- scope of the binding for x
=> let x = 3 * y in x + y substitution: (y,2)
= let x = 3 * 2 in x + 2 apply substitution
=> x + 2 substitution: (x,(3*2))
= (3*2) + 2 apply substitution
= 8 evaluate
3.37.
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
Show Solution
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
------------------------------------------- scope of binding for x
-------------------------- scope of binding for y
--------- scope of binding for z
=> let y = x - 2 in let z = 5 * y in x * y + z substitution: (x,3)
= let y = 3 - 2 in let z = 5 * y in 3 * y + z apply substitution
=> let z = 5 * y in 3 * y + z substitution: (y,(3-2))
= let z = 5 * (3-2) in 3 * (3-2) + z apply substitution
=> 3 * (3-2) + z substitution: (z,5*(3-2))
= 3 * (3-2) + (5*(3-2)) apply substitution
= 3 * 1 + 5 = 8 evaluate
3.38. Now redo the last one, but evaluate any arithmetic expressions as soon as possible:
Show Solution
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
------------------------------------------- scope of binding for x
-------------------------- scope of binding for y
--------- scope of binding for z
=> let y = x - 2 in let z = 5 * y in x * y + z substitution: (x,3)
= let y = 3 - 2 in let z = 5 * y in 3 * y + z apply substitution
= let y = 1 in let z = 5 * y in 3 * y + z evaluate
=> let z = 5 * y in 3 * y + z substitution: (y,1)
= let z = 5 * 1 in 3 * 1 + z apply substitution
= let z = 5 in 3 + z evaluate
=> 3 + z substitution: (z,5)
= 3 + 5 apply substitution
8 evaluate
Correspondence between Lambda and Let Expressions
Now we investigate how let expressions are really "syntactic sugar" for lambda expressions.
3.39. Find a lambda expression which can be reduced in one step and then evaluated to do the exact same thing as
let x = 5 in x * 2 + x
Show the lambda expression and then show how it is evaluated.
Show Solution
= (\x -> x * 2 + x) 5
=> x * 2 + x substitution: (x,5)
= 5 * 2 + 5 apply substitution
= 15 evaluate
3.40. Ditto for:
let y = 2 in let x = 3 * y in x * y
evaluating any arithmetic expressions as soon as possible.
Show Solution
let y = 2 in let x = 3 * y in x * y
(\y -> let x = 3 * y in x * y ) (2)
(\y -> (\x -> x * y) (3 * y)) (2)
=> (\x -> x * y) (3 * y) substitution: (y,2)
= (\x -> x * 2) (3 * 2) apply substitution
= (\x -> x * y) (6) evaluate
x * 2 substitution: (x,6)
6 * 2 apply substitution
12 evaluate
3.41. Ditto for let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z.
Show Solution
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
(\x -> let y = x - 2 in let z = 5 * y in x * y + z ) (3)
(\x -> (\y -> let z = 5 * y in x * y + z ) (x - 2) ) (3)
(\x -> (\y -> (\z -> (x * y + z) (5 * y) ) (x - 2) ) (3)
=> (\y -> (\z -> (x * y + z) (5 * y) ) (x - 2) ) (x,3)
= (\y -> (\z -> (3 * y + z) (5 * y) ) (3 - 2) ) apply subst
= (\y -> (\z -> (3 * y + z) (5 * y) ) (1) ) eval
= (\y -> (\z -> (3 * y + z) (5 * y) ) (1) ) eval
= (\z -> (3 * y + z) (5 * y) ) ) (y,1)
= (\z -> (3 * 1 + z) (5 * 1) ) )
= (\z -> (3 * 1 + z) (5) ) )
= (3 * 1 + z) (z,5) = (3 * 9 + 5)
= (8)
3.42. Ditto for the following let expression:
let x = 3 in let x = 2 * x in x + 1
Show Solution
let x = 3 in let x = 2 * x in x + 1
(\x -> let x = 2 * x in x + 1 ) (3)
(\x -> (\x -> x + 1) (2 * x) ) (3)
=> (\x -> x + 1) (2 * x) (x,3)
= (\x -> x + 1) (2 * 3)
= (\x -> x + 1) (6)
=> (x + 1) (x,6)
= (6 + 1)
= (7)
3.43. Show the scope of the variable y in the let expression in 3.41 and also in its corresponding lambda expression.
Show Solution
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
-------------------------- scope of y
(\x -> (\y -> (\z -> x * y + z ) (5 * y) ) (x - 2) ) 3
-------------------------- scope of y
3.44. Ditto for the two occurrences of x in the let and lambda expressions in 3.42, showing the "hole in scope" for the outer occurrence.
Show Solution
let x = 3 in let x = 2 * x in x + 1
----- scope of inner occurrence of x
----------------- scope of outer occurrence of x
..... "hole in scope" of outer occurrence of x
(\x -> (\x -> x + 1) (2 * x) ) (3)
----- scope of inner occurrence of x
------- -------- scope of outer occurrence of x
..... "hole in scope" of outer occurrence of x
The remaining two problems are optional (not required for Midterm 1)
3.45. Show the correspondence between the Java program in Problem 3.2 and the let expression in Problem 6.14.
Show Solution
public class ScopeTest {
public static void main(String args[]) {
int y = 2; let y = 2 in
int x = 3 * y; let x = 3 * y in
System.out.println(x + y); x + y
}
}
3.46. Ditto for the Python program in Problem 3.8.
Show Solution
x = 3 let x = 3 in
y = x - 2 let y = x - 2 in
z = 5 * y let z = 5 * y in
print( x * y + z ) x * y + z